package fem2.material;

import inf.jlinalg.SolveFailedException;
import inf.text.ArrayFormat;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import fem2.Constraint;
import fem2.Element;
import fem2.MaterialModel;
import fem2.Mesh;
import fem2.MeshUtilities;
import fem2.Model;
import fem2.PatchTestQ4;
import fem2.PatchTestT3;
import fem2.analysis.Analysis;
import fem2.analysis.LinearStaticAnalysis;
import fem2.element.BBarStructuralElement;
import fem2.element.EASStructuralElement;
import fem2.element.StructuralElement;
import fem2.enu.EASModeType;
import fem2.enu.EchoLevelType;
import fem2.enu.State;
import fem2.material.FluencyCriteria;
import fem2.material.LinearIsotropicHardeningRule;
import fem2.material.LinearKinematicHardeningRule;
import fem2.material.VonMisesMM;

@RunWith(Parameterized.class)
public class PatchTestVonMises {

	private Model model;

	@Parameters
	public static Collection<Object[]> parameters() {
		List<Object[]> params = new LinkedList<Object[]>();

		params.add(new Object[] { createModel(createMeshQ4(), 0) });
		params.add(new Object[] { createModel(createMeshT3(), 0) });
		params.add(new Object[] { createModel(createMeshQ4(), 1) });
		params.add(new Object[] { createModel(createMeshT3(), 1) });
		// params.add(new Object[] { createModel(createMeshQ4(), 2) }); // fail
		// params.add(new Object[] { createModel(createMeshT3(), 2) }); // fail
		return params;
	}

	public PatchTestVonMises(Model m) {
		model = m;
	}

	public static MaterialModel createMaterial() {
		double E = 2;
		double nu = 0.3;
		double t = 1.0;
		State ss = State.PLANE_STRAIN;
		double ft = 1.9;
		double H = 4.0;
		double K = 2.0;
		FluencyCriteria isoLaw = new LinearIsotropicHardeningRule(ft, K);
		FluencyCriteria kinLaw = new LinearKinematicHardeningRule(H);
		MaterialModel cm = new VonMisesMM(E, nu, t, 0.0, ss, isoLaw, kinLaw);
		return cm;
	}

	public static Model createModel(Mesh mesh, int elementType) {
		Model M = new Model(mesh);
		MaterialModel mm = createMaterial();

		for (int i = 0; i < mesh.countFaces(); i++) {
			Element e;

			switch (elementType) {
			case 0:
				e = new StructuralElement(mesh.getFace(i), mm);
				break;
			case 1:
				e = new BBarStructuralElement(mesh.getFace(i), mm);
				break;
			case 2:
				e = new EASStructuralElement(mesh.getFace(i), mm, EASModeType.Q1E4);
				break;
			default:
				throw new Error("invalid element type");
			}

			M.addElement(e);
		}

		return M;
	}

	@Test
	public void testSolve1() throws SolveFailedException {
		Mesh mesh = model.getMesh();

		double du = 2;
		Constraint c1 = new Constraint(false, false);
		c1.setValue(0, du);
		Constraint c2 = new Constraint(false, true);
		c2.setValue(0, du);

		mesh.setConstraint(c2, MeshUtilities.seekNodesOnSurface(mesh, 1, 0, -1));
		MeshUtilities.seekNode(mesh, 1, 0).setConstraint(c1);

		Analysis an = new LinearStaticAnalysis(model);
		an.getStrategy().setEchoLevel(EchoLevelType.OUTPUT);
		an.run();

		double[][] Uh = new double[][] { { 0, 0 }, { 2.0, 0 }, { 2.0, -0.967228028754730 },
				{ 0, -0.967228028754730 } };
		for (int i = 0; i < 4; i++) {
			double[] uHatLocal = model.getMesh().getNode(i).getUHatLocal();
			System.out.println(ArrayFormat.format(uHatLocal));
		}
		for (int i = 0; i < 4; i++) {
			double[] uHatLocal = model.getMesh().getNode(i).getUHatLocal();
			Assert.assertEquals(Uh[i][0], uHatLocal[0], 1e-10);
			Assert.assertEquals(Uh[i][1], uHatLocal[1], 1e-10);
		}

		// IVector epsilon = ((StructuralElement)
		// m.getElement(0)).computeTotalStrain(0);
		// IVector sigma = ((StructuralElement) m.getElement(0)).getStress(0);
		// System.out.println("strain = " + epsilon);
		// System.out.println("stress = " + sigma);
	}

	public static Mesh createMeshQ4() {
		return new PatchTestQ4(0).createMesh();
	}

	public static Mesh createMeshT3() {
		return new PatchTestT3(0).createMesh();
	}
}
